package org.bronycorps.mlptss.domain.controller;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.bronycorps.mlptss.domain.model.Character;
import org.bronycorps.mlptss.domain.model.Player;
import org.bronycorps.mlptss.domain.model.event.MatchOverEvent;
import org.bronycorps.mlptss.domain.model.event.MatchOverEventListener;
import org.bronycorps.mlptss.domain.model.type.GameState;
import org.bronycorps.mlptss.domain.model.type.PlayerType;
import org.bronycorps.mlptss.manager.BulletFactory;
import org.bronycorps.mlptss.manager.CharacterFactory;
import org.bronycorps.mlptss.manager.ScreenLoader;
import org.bronycorps.mlptss.manager.WaveFactory;
import org.bronycorps.mlptss.manager.preferences.AssetsConfiguration.CharacterConfiguration;
import org.bronycorps.mlptss.manager.preferences.AssetsConfiguration.EnemyConfiguration;
import org.bronycorps.mlptss.manager.preferences.AssetsConfiguration.SpecialAttackConfiguration;
import org.bronycorps.mlptss.manager.preferences.EpisodeConfiguration;
import org.bronycorps.mlptss.manager.preferences.GameConfiguration;
import org.bronycorps.mlptss.manager.preferences.LevelConfiguration;
import org.bronycorps.mlptss.screen.GameOverScreen;
import org.bronycorps.mlptss.screen.LevelScreen;
import org.bronycorps.mlptss.screen.MenuScreen;

public class GameController {

	private static GameController instance = null;

	public static GameController getInstance() {
		if (instance == null) {
			instance = new GameController();
		}
		return instance;
	}

	private EpisodeConfiguration currentEpisode;
	private int currentLevelId;
	private EnemyConfiguration enemyConfiguration;

	private GameConfiguration gameConfig;

	private List<MatchOverEventListener> gameOverEventListeners = new ArrayList<MatchOverEventListener>();

	private boolean isMatchOver = false;

	private ArrayList<LevelConfiguration> levels;

	private Player[] players = new Player[2];

	private int round = 0;
	private GameState state = GameState.MENU;
	private WaveFactory waveFactory;

	private GameController() {
		super();
	}

	public boolean addListener(MatchOverEventListener e) {
		return this.gameOverEventListeners.add(e);
	}

	public void clearListeners() {
		this.gameOverEventListeners.clear();
	}

	public void continueGame() {
		this.players[0].resetContinues();
		this.players[0].setHighscore(0);

		for (Player player : this.players) {
			player.reset();
		}

		this.state = GameState.GAME;
		clearListeners();
		this.isMatchOver = false;
		ScreenLoader.getInstance().loadScreen(new LevelScreen());
	}

	public void endRound() {

		this.round++;
		if (this.players[0].getContinues() < 0) {
			// player game over
			this.state = GameState.GAMEOVER;
			// end of the match
			ScreenLoader.getInstance().loadScreen(new GameOverScreen());
		} else if (this.players[1].getContinues() < 0) {
			if (this.currentLevelId < this.levels.size() - 1) {
				// go next level
				this.players[0].restoreLife();
				loadLevel(this.currentLevelId + 1);
			} else {
				// victory
				clearListeners();
				ScreenLoader.getInstance().loadScreen(new MenuScreen());
			}
		} else {
			// let's do another round

			for (Player player : this.players) {
				player.reset();
			}

			this.state = GameState.GAME;
			clearListeners();
			this.isMatchOver = false;
			ScreenLoader.getInstance().loadScreen(new LevelScreen());
		}

	}

	public void fireGameOver(Player loser) {
		this.isMatchOver = true;

		loser.removeContinue();

		MatchOverEvent event = new MatchOverEvent(loser);
		for (MatchOverEventListener listener : this.gameOverEventListeners) {
			listener.onMatchOver(event);
		}
	}

	public EnemyConfiguration getEnemyConfiguration() {
		return this.enemyConfiguration;
	}

	public Player getOtherPlayer(Player player) {
		return this.players[(player.getIdPlayer() + 1) % 2];
	}

	public Player getPlayer(int noPlayer) {
		return this.players[noPlayer];
	}

	public int getRound() {
		return this.round;
	}

	public GameState getState() {
		return this.state;
	}

	public WaveFactory getWaveFactory() {
		return this.waveFactory;
	}

	private void initializeLevel() {
		LevelConfiguration levelConfiguration = this.levels
				.get(this.currentLevelId);
		selectPlayer(
				1,
				CharacterFactory
						.getCharacter(selectOpponentCharacter(levelConfiguration)),
				PlayerType.COMPUTER);
		selectEnemyType(this.gameConfig.getCurrentMod()
				.getAssetsConfiguration()
				.getEnemyByName(levelConfiguration.getEnemyType()));
	}

	public boolean isMatchOver() {
		return this.isMatchOver;
	}

	private void loadLevel(int levelId) {
		this.currentLevelId = levelId;
		this.round = 0;
		initializeLevel();
		this.state = GameState.GAME;
		clearListeners();
		this.isMatchOver = false;
		ScreenLoader.getInstance().loadScreen(new LevelScreen());
	}

	public void loadMainMenu() {
		this.state = GameState.MENU;

		ScreenLoader.getInstance().loadScreen(new MenuScreen());
	}

	public void newGame() {

		this.waveFactory = new WaveFactory();

		this.gameConfig = GameConfiguration.getInstance();
		this.currentEpisode = this.gameConfig.getCurrentMod().getEpisodes()
				.get(0);
		this.levels = this.currentEpisode.getLevels();
		this.currentLevelId = 0;

		loadLevel(0);
	}

	public boolean removeListener(MatchOverEventListener o) {
		return this.gameOverEventListeners.remove(o);
	}

	public void selectEnemyType(EnemyConfiguration enemyConfiguration) {
		this.enemyConfiguration = enemyConfiguration;

	}

	private CharacterConfiguration selectOpponentCharacter(
			LevelConfiguration level) {
		ArrayList<CharacterConfiguration> characters = this.gameConfig
				.getCurrentMod().getAssetsConfiguration().getCharacters();
		List<String> selection = new ArrayList<String>();
		for (CharacterConfiguration characterConfiguration : characters) {
			selection.add(characterConfiguration.getName());
		}

		if (!level.getIncludeOpponents().isEmpty()) {
			String includes = level.getIncludeOpponents();
			String[] includeArray = includes.split(",");
			selection.retainAll(Arrays.asList(includeArray));
		}

		if (!level.getExcludeOpponents().isEmpty()) {
			String excludes = level.getExcludeOpponents();
			String[] excludeArray = excludes.split(",");
			selection.removeAll(Arrays.asList(excludeArray));
		}

		int id = (int) (Math.random() * selection.size());
		return this.gameConfig.getCurrentMod().getAssetsConfiguration()
				.getCharByName(selection.get(id));

	}

	public void selectPlayer(int noPlayer, Character model,
			PlayerType playerType) {
		this.players[noPlayer] = new Player(noPlayer, model, playerType);

		if (BulletFactory.getSpecialBulletType(model.getSpecialBulletName()) == null) {
			SpecialAttackConfiguration specialAttackConfiguration = GameConfiguration
					.getInstance().getCurrentMod().getAssetsConfiguration()
					.getSpecialAttackByName(model.getSpecialBulletName());
			BulletFactory.loadSpecialBulletType(specialAttackConfiguration);
		}
	}

	public void setMatchOver(boolean isMatchOver) {
		this.isMatchOver = isMatchOver;
	}

}
